﻿/*
   Copyright 2013 Dmitry Bratus

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jurassic.Library;
using Jurassic;
using Flower.Directory.Scripting;
using Flower.Services.Data;
using Flower.Directory.Util;
using System.IO;

namespace Flower.Directory.Management.Scripting
{
    internal class Dump : ClrFunction
    {
        public Dump(ScriptEngine engine)
            : base
            (
                engine.Function.InstancePrototype,
                "dump",
                engine.Object.InstancePrototype
            )
        {
        }

        [JSCallFunction]
        public void Exec(string pid, object dest)
        {
            try
            {
                var dirObj = Engine.GetGlobalValue<ObjectInstance>("$dir");
                var dirObjTyped = dirObj as DirectoryObject;

                if (dirObjTyped == null) 
                {
                    throw new JavaScriptException(Engine, "Error", "No directory object found.");
                }

                DirectoryEntryInfo proc = dirObjTyped.Directory.GetInfo(pid);

                if (proc == null || proc.Type != DirectoryEntryTypes.Process)
                {
                    throw new JavaScriptException(Engine, "Error", string.Format("The entry {0} is not found or its not a process.", pid));
                }

                string destPath = 
                    (dest != null && !(dest is Null) && !(dest is Undefined)) ? dest.ToString() : pid;

                if (!System.IO.Directory.Exists(destPath))
                {
                    System.IO.Directory.CreateDirectory(destPath);
                }

                string statesPath = destPath + "\\States";

                if (!System.IO.Directory.Exists(statesPath))
                {
                    System.IO.Directory.CreateDirectory(statesPath);
                }

                const long statesBatchSize = 1;
                const long logBatchSize = 100;

                IList<DirectoryEntry> entries;
                long offset = 0;
                int statesCnt = 1;

                do
                {
                    long lim = offset + statesBatchSize;

                    entries =
                        dirObjTyped
                            .Directory
                            .GetChildren
                            (
                                pid, 
                                DirectoryEntryTypes.State, 
                                SortProperty.Default, 
                                SortOrder.Asc, 
                                offset, 
                                lim
                            );

                    offset = lim;

                    foreach (State state in entries)
                    {
                        string fileName = string.Format("{0}\\{1:d6}_{2}.xml", statesPath, statesCnt, state.Name);

                        using (var outStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write))
                        {
                            state.Data.WriteAsReadableXml(outStream);
                            outStream.Flush();
                        }
                    }

                    statesCnt++;
                } while(entries.Count > 0);

                string logPath = string.Concat("/Sets/Local/", pid, "/Log");
                offset = 0;

                using (var writer = new StreamWriter(destPath + "\\Log.txt", false, Encoding.UTF8))
                {
                    do
                    {
                        long lim = offset + logBatchSize;

                        entries =
                            dirObjTyped
                                .Directory
                                .GetChildren
                                (
                                    logPath,
                                    DirectoryEntryTypes.Message,
                                    SortProperty.Default,
                                    SortOrder.Asc,
                                    offset,
                                    lim
                                );

                        offset = lim;

                        foreach (Message logEntry in entries)
                        {
                            writer.WriteLine(logEntry.Data.To<string>());
                        }

                        statesCnt++;
                    } while (entries.Count > 0);

                    writer.Flush();
                }
            }
            catch (JavaScriptException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message, ex);
            }
        }
    }
}
